/*
 * Copyright (C) 2021 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package net.sqlcipher.example.slice;

import ohos.aafwk.ability.AbilitySlice;
import ohos.aafwk.content.Intent;
import ohos.agp.colors.RgbColor;
import ohos.agp.components.Button;
import ohos.agp.components.Component;
import ohos.agp.components.DirectionalLayout;
import ohos.agp.components.DirectionalLayout.LayoutConfig;
import ohos.agp.components.Text;
import ohos.agp.components.element.ShapeElement;
import ohos.agp.utils.Color;
import ohos.data.rdb.ValuesBucket;
import ohos.data.resultset.ResultSet;
import ohos.hiviewdfx.HiLog;
import ohos.hiviewdfx.HiLogLabel;

import net.sqlcipher.database.SQLiteDatabase;
import net.sqlcipher.database.SQLiteQueryStats;
import net.sqlcipher.database.SQLiteStatement;
import net.sqlcipher.example.SqlcipherContract;
import net.sqlcipher.example.SqlcipherDbHelper;

import java.io.File;
import java.security.SecureRandom;
import java.util.Arrays;
import java.util.Random;

/**
 * MainAbilitySlice
 */
public class MainAbilitySlice extends AbilitySlice {
    private static final HiLogLabel LABEL = new HiLogLabel(HiLog.LOG_APP, 0x002A, "MainAbilitySlice");

    @Override
    public void onStart(Intent intent) {
        super.onStart(intent);
        DirectionalLayout directionLayout = new DirectionalLayout(this);
        LayoutConfig config = new LayoutConfig(LayoutConfig.MATCH_PARENT, LayoutConfig.MATCH_PARENT);
        directionLayout.setLayoutConfig(config);
        Text text = new Text(this);
        text.setText("Encrypted Db created");
        text.setTextColor(Color.BLACK);
        text.setTextSize(40);
        directionLayout.addComponent(text);
        super.setUIContent(directionLayout);
    }

    private void testInsertBlob() {
        SQLiteDatabase db = openDbcreateTable();
        SecureRandom random = new SecureRandom();
        byte[] randomData = new byte[20];
        random.nextBytes(randomData);
        db.execSQL("insert into t1(a,b) values(?, ?);", new Object[]{"one for the money", randomData});
        ResultSet cursor = db.rawQuery("select * from t1 where b = ?;", new Object[]{randomData});
        if (cursor != null) {
            if (cursor.goToFirstRow()) {
                String first = cursor.getString(0);
                byte[] bytes = cursor.getBlob(1);
                HiLog.debug(LABEL, "PASS : " + (first.equals("one for the money") && Arrays.equals(randomData, bytes)));
            }
            cursor.close();
        }
    }

    private SQLiteDatabase openDbcreateTable() {
        SQLiteDatabase db = SqlcipherDbHelper.getInstance(this).getWritableDatabase("somePass");
        db.execSQL("DROP TABLE IF EXISTS t1;");
        db.execSQL("create table t1(a,b);");
        return db;
    }

    private void testInsertString() {
        SQLiteDatabase db = openDbcreateTable();
        db.execSQL("insert into t1(a,b) values(?, ?);", new Object[]{"one for the money", "two for the show"});
        ResultSet cursor = db.rawQuery("select * from t1 where b = ?;", new Object[]{"two for the show"});
        if (cursor != null) {
            if (cursor.goToFirstRow()) {
                String first = cursor.getString(0);
                String second = cursor.getString(1);
                HiLog.debug(LABEL, "PASS : " + (first.equals("one for the money") &&
                        second.equals("two for the show")));
            }
            cursor.close();
        }
    }

    private void testInsertDouble() {
        SQLiteDatabase db = openDbcreateTable();
        db.execSQL("insert into t1(a,b) values(?, ?);", new Object[]{"one for the money", 2.0d});
        ResultSet cursor = db.rawQuery("select * from t1 where b = ?;", new Object[]{2.0d});
        if (cursor != null) {
            if (cursor.goToFirstRow()) {
                String first = cursor.getString(0);
                Double second = cursor.getDouble(1);
                HiLog.debug(LABEL, "PASS :" + (first.equals("one for the money") && second == 2.0d));
            }
            cursor.close();
        }
    }

    private void testInsertLong() {
        SQLiteDatabase db = openDbcreateTable();
        db.execSQL("insert into t1(a,b) values(?, ?);", new Object[]{"one for the money", 2L});
        ResultSet cursor = db.rawQuery("select * from t1 where b = ?;", new Object[]{2L});
        if (cursor != null) {
            if (cursor.goToFirstRow()) {
                String first = cursor.getString(0);
                long second = cursor.getLong(1);
                HiLog.error(LABEL, "PASS" + (first.equals("one for the money") && second == 2L));
            }
            cursor.close();
        }
    }

    private void testInsertFloat() {
        SQLiteDatabase db = openDbcreateTable();
        db.execSQL("insert into t1(a,b) values(?, ?);", new Object[]{"one for the money", 2.25f});
        ResultSet cursor = db.rawQuery("select * from t1 where b = ?;", new Object[]{2.25f});
        if (cursor != null) {
            if (cursor.goToFirstRow()) {
                String first = cursor.getString(0);
                float second = cursor.getFloat(1);
                HiLog.debug(LABEL, "PASS" + (first.equals("one for the money") && second == 2.25f));
            }
            cursor.close();
        }
    }

    /**
     * createButton
     * @param button button
     * @param text text
     */
    public void createButton(Button button, String text) {
        button.setText(text);
        button.setTextSize(40);
        ShapeElement background = new ShapeElement();
        background.setRgbColor(RgbColor.fromArgbInt(0xFF41C7D6));
        background.setCornerRadius(20);
        button.setBackground(background);
        button.setPadding(30, 30, 30, 30);
    }

    private void testInsertBool() {
        SQLiteDatabase db = openDbcreateTable();
        db.execSQL("insert into t1(a,b) values(?, ?);", new Object[]{"one for the money", true});
        ResultSet cursor = db.rawQuery("select * from t1 where b = ?;", new Object[]{true});
        if (cursor != null) {
            if (cursor.goToFirstRow()) {
                String first = cursor.getString(0);
                int second = cursor.getInt(1);
                HiLog.debug(LABEL, "PASS" + (first.equals("one for the money") && second == 1));
            }
            cursor.close();
        }
    }

    private void testInsertMultipleRows() {
        SQLiteDatabase db = SqlcipherDbHelper.getInstance(this).getWritableDatabase("somePass");
        ValuesBucket values = new ValuesBucket();
        SecureRandom rand = new SecureRandom();
        values.putInteger(SqlcipherContract.SqlEntry.COLUMN_NAME_ENTRY_ID, rand.nextInt(100));
        values.putString(SqlcipherContract.SqlEntry.COLUMN_NAME_TITLE, "Easter Bunny has escaped!");
        values.putString(SqlcipherContract.SqlEntry.COLUMN_NAME_SUBTITLE,
            "A thrilling story which proves how fragile our hearts are...");
        db.insert(SqlcipherContract.SqlEntry.TABLE_NAME, null, values);

        ResultSet cursor = db.rawQuery("SELECT * FROM '" + SqlcipherContract.SqlEntry.TABLE_NAME + "';", null);
        while (cursor.goToNextRow())  {
        HiLog.error(LABEL, " 1: " + cursor.getInt(1));
        HiLog.error(LABEL, " 2: " + cursor.getString(2));
        HiLog.error(LABEL, " 3: " + cursor.getString(3));
        }
        HiLog.debug(LABEL, "OnConfigure Test Result :" + SqlcipherDbHelper.getInstance(this).onConfigureCalled);
        cursor.close();
        db.close();
    }

    private void testPragmaCipherVersion() {
        SQLiteDatabase db = SqlcipherDbHelper.getInstance(this).getWritableDatabase("somePass");
        ResultSet cursor = db.rawQuery("PRAGMA cipher_version", new String[] {});
        if (cursor != null) {
            cursor.goToNextRow();
            String cipherVersion = cursor.getString(0);
            if (cipherVersion.contains("4.4.0")) {
                HiLog.debug(LABEL, "PASS");
            }
            cursor.close();
        }
    }

    /**
     * testCipherVersion
     */
    private void testCipherVersion() {
        SQLiteDatabase db = SqlcipherDbHelper.getInstance(this).getWritableDatabase("somePass");
        ResultSet cursor = db.query("PRAGMA cipher_version;", new String[]{});
        String version = "";
        if (cursor != null) {
            cursor.goToFirstRow();
            version = cursor.getString(0);
            cursor.close();
        }
        int expectedValue = version.contains("FIPS") ? 1 : 0;

        ResultSet cursorFips = db.query("PRAGMA cipher_fips_status;", new String[]{ });
        int status = 0;
        if (cursorFips != null) {
            cursorFips.goToFirstRow();
            status = cursorFips.getInt(0);
            cursorFips.close();
        }
        if (status == expectedValue) {
            HiLog.debug(LABEL, "PASS: ");
        }
    }

    /**
     * testInsertByteArray
     */
    public void testInsertByteArray() {
        SQLiteDatabase db = openDbcreateTable();
        db.execSQL("INSERT INTO t1(a,b) VALUES(?, ?);",
            new Object[]{generateRandomByteArray(256), generateRandomByteArray(256)});
        db.execSQL("INSERT INTO t1(a,b) VALUES(?, ?);",
            new Object[]{generateRandomByteArray(1024), generateRandomByteArray(64)});
        SQLiteQueryStats result = db.getQueryStats("SELECT * FROM t1;", new Object[]{});
        db.close();
        if (result.getTotalQueryResultSize() > 0 && result.getLargestIndividualRowSize() > 0) {
            HiLog.debug(LABEL, "PASS");
        }
    }

    /**
     * generateRandomByteArray
     * @param size size
     * @return byte[]
     */
    protected byte[] generateRandomByteArray(int size) {
        SecureRandom random = new SecureRandom();
        int internalSize = 64;
        if (size > internalSize) {
            internalSize = size;
        }
        byte[] data = new byte[internalSize];
        random.nextBytes(data);
        return data;
    }

    /**
     * testOpenReadOnly
     */
    public void testOpenReadOnly() {
        File dbPathFile = new File (SqlcipherDbHelper.DATABASE_NAME);
        if (!dbPathFile.exists()) {
            dbPathFile.getParentFile().mkdirs();
        }
        SQLiteDatabase db = SQLiteDatabase.openDatabase(SqlcipherDbHelper.DATABASE_NAME, "somePass",
            null, SQLiteDatabase.OPEN_READONLY);
        boolean opened = db.isOpen();
        db.close();
        HiLog.debug(LABEL, "PASS : " + opened);
    }

    /**
     * testUpdateRow
     */
    public void testUpdateRow() {
        SQLiteDatabase database = openDbcreateTable();
        database.execSQL("insert into t1(a, b) values (?, ?)", new Object[]{"s1", new Integer(100)});
        SQLiteStatement st = database.compileStatement("update t1 set b = 101 where b = 100");
        long recs = st.executeUpdateDelete();
        database.close();
        HiLog.debug(LABEL, "PASS : " + (recs == 1));
    }

    /**
     * testDeleteRow
     */
    public void testDeleteRow() {
        SQLiteDatabase database = openDbcreateTable();
        database.execSQL("INSERT INTO t1(a,b) VALUES(?,?);", new Object[] {"foo", "bar"});
        database.beginTransaction();
        String table = "t1";
        int count = database.delete(table, "a = ?", new Object[]{"foo"});
        database.close();
        HiLog.debug(LABEL, "Deleted count" + count);
    }

    @Override
    public void onStop() {
        super.onStop();
    }
}
